#ifndef MESSAGE_H
#define MESSAGE_H

#include <map>
#include <vector>

#include <wx/object.h>
#include <wx/arrstr.h>
#include <wx/tokenzr.h>
#include <wx/thread.h>

#include "tux.h"
#include "Util.h"

class SessionConnection;

class MessageBuffer;

class MsnMessage;

typedef std::map<wxString, wxString> StringMap; 
typedef std::vector<std::pair<wxString, wxString> > StringPairVector;

class MsnMessageFactory
{
public:
    static MsnMessageFactory& Get(){return instance;}

    MsnMessage* ParseMessage(MessageBuffer& chunk);

	MsnMessage* ParseMessage(char* buf, int len);

protected:
    MsnMessageFactory();

	wxString GetHeader(char* buf, int len);

    wxString GetContentType(const wxString& chunk);

private:
    static MsnMessageFactory instance;
    StringMap messageTypeMap;
};

class MessageBuffer
{
private:
    char* buf;
    unsigned int size;
    unsigned int offset;

public:
    MessageBuffer(unsigned int _size);
    ~MessageBuffer();

    wxString ReadLine();
    wxString ReadHeader();

    wxString ReadBody();

    wxString ReadString(unsigned int len);

    unsigned int GetOffset(){return offset;}
    void SetOffset(int pos){offset = pos;}
    char* GetBuffer(){return buf;}
    unsigned int GetSize(){return size;}

    wxString ToString();
};


class MsnMessage : public wxObject
{
protected:
    time_t createTime;
    wxString contact;

    SessionConnection* session;

	wxString messageType;

	/*
	The first parameter is the type of acknowledgement you would like to receive. 
	It can be either U, N, or A. If it is U, you will receive no acknowledgement. 
	If it is N, you will receive acknowledgement only if the message was not properly received. 
	If it is A, you will receive acknowledgement whether it was properly received or not. 
	This parameter is case-sensitive (must be uppercase, or the switchboard will close the connection without sending the message). 
	*/
	//for P2P message the ack type must be 'D'.
	wxString ackType;

	StringPairVector headProperties;
	StringPairVector bodyProperties;

public:
    MsnMessage();

	MsnMessage(const MsnMessage& msg);

    virtual ~MsnMessage(){}

	wxString GetAckType(){return ackType;}

    SessionConnection* GetSession(){return session;}
    void SetSession(SessionConnection* conn){session = conn;}

public:
	wxString GetContentType();

	wxString GetHeadProperty(const wxString& name);
    void SetHeadProperty(const wxString& name, const wxString& value);

    wxString GetProperty(const wxString& name);
	bool GetPropertyAsBool(const wxString& name);
    void SetProperty(const wxString& name, const wxString& value);

public:
    void DecodeProperties(StringPairVector& vec, const wxString& props);
	void EncodeProperties(StringPairVector& vec, wxMemoryBuffer& buf);

	size_t GetMessageLength();
	size_t GetBodyLength();

    virtual wxString ToString();
    
	virtual void Decode(char* buf, unsigned int len);
	virtual void DecodeHead(char* head, unsigned int len);
	virtual void DecodeBody(char* body, unsigned int len);

	virtual void Encode(wxMemoryBuffer& buf);
	virtual void EncodeHead(wxMemoryBuffer& buf);
	virtual void EncodeBody(wxMemoryBuffer& buf);

public:
    wxString GetContact(){return contact;}
    void SetContact(const wxString& from){contact = from;}

    time_t GetCreateTime(){return createTime;}
};

class ProfileMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(ProfileMessage)

public:
	ProfileMessage(){messageType = _T("text/x-msmsgsprofile");}
    virtual ~ProfileMessage(){};

	virtual void DecodeBody(char* body, unsigned int len);

};

class InstantMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(InstantMessage)

protected:
    wxString content;
    wxString fontName;

    bool strikeout;
    bool underline;
    bool italic;
    bool bold;

    unsigned char colorBlue; //bgr
    unsigned char colorGreen; //bgr
    unsigned char colorRed; //bgr

    wxString CS;
    wxString fontPF;

public:
    InstantMessage();

    virtual ~InstantMessage(){}

	virtual void DecodeBody(char* body, unsigned int len);

	virtual void EncodeHead(wxMemoryBuffer& buf);
	virtual void EncodeBody(wxMemoryBuffer& buf);

    wxString GetContent(){return content;}
    void SetContent(const wxString& str){content = str;}

    wxString GetFontName(){return fontName;}
    void SetFontName(const wxString& fn){fontName = fn;}

    bool IsStrikeout(){return strikeout;}
    bool IsUnderline(){return underline;}
    bool IsItalic(){return italic;}
    bool IsBold(){return bold;}

    unsigned char GetColorBlue(){return colorBlue;}
    unsigned char GetColorGreen(){return colorGreen;}
    unsigned char GetColorRed(){return colorRed;}
    void SetColorBlue(unsigned char c){colorBlue = c;}
    void SetColorGreen(unsigned char c){colorGreen = c;}
    void SetColorRed(unsigned char c){colorRed = c;}
};


class ControlMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(ControlMessage)

public:
    ControlMessage(){messageType = _T("text/x-msmsgscontrol");}
    virtual ~ControlMessage(){}
	virtual void ParseContent(const wxString& content){};

	virtual void EncodeHead(wxMemoryBuffer& buf);

};

class InviteMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(InviteMessage)
public:
    InviteMessage();
	virtual ~InviteMessage(){}

	virtual void DecodeBody(char* body, unsigned int len);

	virtual void EncodeBody(wxMemoryBuffer& buf);

public:
    wxString GetInvitationCommand(){return GetProperty(_T("Invitation-Command"));}
    wxString GetInvitationCookie(){return GetProperty(_T("Invitation-Cookie"));}

    wxString GetFile(){return GetProperty(_T("Application-File"));}
    int GetFileSize(){return StringUtil::ToLong(GetProperty(_T("Application-FileSize")));}

    wxString GetConnectivity(){return GetProperty(_T("Connectivity"));}
};

class SLPMessage;

struct P2PHeader
{
	unsigned int sessionID;
	unsigned int ID;
	unsigned long long dataOffset;
	unsigned long long dataSize;
	unsigned int messageLength;
	unsigned int flag;
	unsigned int ackID;
	unsigned int ackUID;
	unsigned long long ackDataSize;

	wxString ToReadableFormat();

};

class P2PMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(P2PMessage)

protected:
	P2PHeader header;

	int footer;

	wxMemoryBuffer slpBuffer;
    SLPMessage* slpMessage;

public:
	P2PMessage();

	P2PMessage(const P2PMessage& msg);

	virtual ~P2PMessage();

	wxString GetDestination(){return GetHeadProperty(_T("P2P-Dest"));}
	void SetDestination(const wxString& dest){SetHeadProperty(_T("P2P-Dest"),  dest);}

	wxMemoryBuffer& GetSlpBuffer(){return slpBuffer;};

	SLPMessage* GetSlpMessage(){return slpMessage;}

	void SetSlpMessage(SLPMessage* msg){slpMessage = msg;}

	P2PHeader* GetHeader(){return &header;}

	int GetFooter(){return footer;}
	void SetFooter(int value){footer = value;}

	bool IsDataMessage(){return header.flag == 0x1000030 || header.flag == 0x20;}

	bool IsDataPreparationMessage(){return footer == 1;}

public:
	void DecodeSlpMessage();

	virtual wxString ToString();

	virtual void DecodeBody(char* buf, unsigned int len);
	
	virtual void EncodeHead(wxMemoryBuffer& buf);

	virtual void EncodeBody(wxMemoryBuffer& buf);

	/*
		The 48-byte binary header consists of 6 DWORDs and 3 QWORDS, 
		which are all in little endian (little end first) order, 
		where a DWORD is a 32-bit (4 byte) unsigned integer and QWORD is 64 bits (8 bytes).
	*/
	void DecodeBinaryHeader(char* buf);
	void DecodeBinaryFooter(char* buf);
	void EncodeBinaryHeader(wxMemoryBuffer& buf);
	void EncodeBinaryFooter(wxMemoryBuffer& buf);
};

class SLPMessage : public MsnMessage
{
DECLARE_DYNAMIC_CLASS(SLPMessage)

protected:
	wxString method;

public:
	SLPMessage(){};
	SLPMessage(const SLPMessage& msg) : MsnMessage(msg)
	{
		DEBUG(_T("SLPMessage copy constructor"));
		method = msg.method;
	}

	virtual ~SLPMessage(){};

	void SetMethod(const wxString& str){method = str;}
	wxString GetMethod(){return method;}

	virtual void Decode(char* buf, unsigned int len);
	virtual void Encode(wxMemoryBuffer& buf);

	virtual void EncodeHead(wxMemoryBuffer& buf);
	virtual void EncodeBody(wxMemoryBuffer& buf);

	virtual wxString ToString();
};


class P2PInvitationMessage : public P2PMessage
{
private:
    //message header
	wxString to;
	wxString from;
    wxString via;
    int cseq;
    wxString callID;
    int maxForwards;
    wxString contentType;
    int contentLength;

    //message body
    wxString eufGuid;
    wxString sessionID;
    wxString appID;
    wxString context;

};


#endif
